// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_entity_terrain_region.h"
#include "mk_entity_terrain.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_render_device.h"
#include "mk_math.h"
#include "mk_render_input.h"
#include "mk_hardware_buffer.h"
#include "mk_shader.h"
#include "mk_texture.h"
#include "mk_resource_manager.h"
#include "mk_terrain_height_brush.h"
#include "mk_terrain_layer_brush.h"
#include "mk_painter.h"
#include "mk_texture.h"
#include "mk_camera.h"
#include "mk_material.h"
#include "mk_mp_fog.h"

#define __LAYER_TEX_NAME_PREFIX "texLayer"
#define __LAYER_BLEND_TEX_NAME "texBlendWeight"
#define __LIGHT_MAP_TEX_NAME "texLightMap"

namespace MK3DE
{
	CEntityTerrainRegion::CEntityTerrainRegion(CScene* pScene, CEntityTerrain* pEntityTerrain)
		: CEntity(pScene)
	{
		_pEntityTerrain = pEntityTerrain;
		_coreTerrainRegion = NULL;
		_vbPosition = NULL;
		_vbNormal = NULL;
		_vbUV = NULL;
		_ibIndex = NULL;
		_renderInput = NULL;
		_renderInputShadow = NULL;
		_shader = NULL;
		_regionX = 0;
		_regionY = 0;
	}

	CEntityTerrainRegion::~CEntityTerrainRegion()
	{
		SAFE_DELETE(_coreTerrainRegion);

		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		SAFE_RELEASE(_vbPosition);
		SAFE_RELEASE(_vbNormal);
		SAFE_RELEASE(_vbUV);
		SAFE_RELEASE(_ibIndex);
		rd->DestroyRenderInput(_renderInput);
		rd->DestroyRenderInput(_renderInputShadow);
		SAFE_RELEASE(_shader);

		for (int i = 0; i < (int)_vecTexture.size(); ++i)
			SAFE_RELEASE(_vecTexture[i]);
		_vecTexture.clear();
	}

	BOOL CEntityTerrainRegion::Create(int regionX, int regionY, const char* shaderFileName)
	{
		BOOL ret = FALSE;
		_coreTerrainRegion = new CTerrainRegion();
		MK_PROCESS_ERROR(_coreTerrainRegion);

		ret = _coreTerrainRegion->CreateMesh(regionX, regionY);
		MK_PROCESS_ERROR(ret);

		CRenderDevice* rd = GetEngine()->GetRenderDevice();
		CResourceManager* rm = GetEngine()->GetResourceManager();

		ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR3), TERRAIN_REGION_VERTEX_COUNT, HBU_STATIC, _vbPosition);
		MK_PROCESS_ERROR(ret);

		ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR3), TERRAIN_REGION_VERTEX_COUNT, HBU_STATIC, _vbNormal);
		MK_PROCESS_ERROR(ret);

		ret = rm->GetVertexBuffer(sizeof(D3DXVECTOR2), TERRAIN_REGION_VERTEX_COUNT, HBU_STATIC, _vbUV);
		MK_PROCESS_ERROR(ret);

		ret = rm->GetIndexBuffer(TERRAIN_REGION_FACE_COUNT * 3, IT_32BIT, HBU_STATIC, _ibIndex);
		MK_PROCESS_ERROR(ret);

		DWORD* pi;
		ret = _ibIndex->Lock((void*&)pi, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		_coreTerrainRegion->FillFaceBuffer((CMesh::Face*)pi, TERRAIN_REGION_FACE_COUNT);
		ret = _ibIndex->Unlock();
		MK_PROCESS_ERROR(ret);

		RenderInputElemDesc rie[] = { 
			RenderInputElemDesc(SU_POSITION, 0, 0, 0),
			RenderInputElemDesc(SU_NORMAL, 0, 0, 1),
			RenderInputElemDesc(SU_TEXCOORD, 0, 0, 2)
		};

		// create render input for terrain
		ret = rd->CreateRenderInput(rie, 3, _renderInput);
		MK_PROCESS_ERROR(ret);

		ret = _renderInput->SetVertexBuffer(0, _vbPosition);
		MK_PROCESS_ERROR(ret);

		ret = _renderInput->SetVertexBuffer(1, _vbNormal);
		MK_PROCESS_ERROR(ret);

		ret = _renderInput->SetVertexBuffer(2, _vbUV);
		MK_PROCESS_ERROR(ret);

		_renderInput->SetIndexBuffer(_ibIndex);

		// create render input for shadow.
		ret = rd->CreateRenderInput(rie, 1, _renderInputShadow);
		MK_PROCESS_ERROR(ret);

		ret = _renderInputShadow->SetVertexBuffer(0, _vbPosition);
		MK_PROCESS_ERROR(ret);

		_renderInputShadow->SetIndexBuffer(_ibIndex);

		ret = GetEngine()->GetResourceManager()->GetShaderFromFile(shaderFileName, _shader);
		MK_PROCESS_ERROR(ret);

		_regionX = regionX;
		_regionY = regionY;

		UpdateLocalAABB();

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::LoadHeightMap(const char* fileName)
	{
		BOOL ret = _coreTerrainRegion->LoadHeightMap(fileName);
		MK_PROCESS_ERROR(ret);

		UpdateLocalAABB();

		return TRUE;
Exit0:
		return FALSE;
	}

	void CEntityTerrainRegion::UpdateLocalAABB()
	{
		_localAABB.Clear();
		for (DWORD y = 0; y < TERRAIN_REGION_WIDTH_VERTEX; y++)
		{
			for (DWORD x = 0; x < TERRAIN_REGION_HEIGHT_VERTEX; x++)
			{
				D3DXVECTOR3 pos;
				_coreTerrainRegion->GetVertexPosition(pos, x, y);
				_localAABB.Merge(&pos);
			}
		}

		UpdateWorldAABB();
	}

	BOOL CEntityTerrainRegion::SaveHeightMap(const char* fileName)
	{
		return _coreTerrainRegion->SaveHeightMap(fileName);
	}

	BOOL CEntityTerrainRegion::UpdatePositionVertexBuffer()
	{
		CMesh::Position* pos;
		BOOL ret = _vbPosition->Lock((void*&)pos, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		ret = _coreTerrainRegion->FillPositionBuffer(pos, TERRAIN_REGION_VERTEX_COUNT);
		MK_PROCESS_ERROR(ret);

		ret = _vbPosition->Unlock();
		MK_PROCESS_ERROR(ret);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::UpdateNormalVertexBuffer()
	{
		CMesh::Normal* normal;
		BOOL ret = _vbNormal->Lock((void*&)normal, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		ret = _coreTerrainRegion->FillNormalBuffer(normal, TERRAIN_REGION_VERTEX_COUNT);
		MK_PROCESS_ERROR(ret);

		ret = _vbNormal->Unlock();
		MK_PROCESS_ERROR(ret);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::UpdateTextureCoordVertexBuffer()
	{
		CMesh::TextureCoord* uv;
		BOOL ret = _vbUV->Lock((void*&)uv, 0, 0, LO_READ_WRITE);
		MK_PROCESS_ERROR(ret);

		ret = _coreTerrainRegion->FillTextureCoordBuffer(uv, TERRAIN_REGION_VERTEX_COUNT);
		MK_PROCESS_ERROR(ret);

		ret = _vbUV->Unlock();
		MK_PROCESS_ERROR(ret);

		return TRUE;
Exit0:
		return FALSE;
	}

	void CEntityTerrainRegion::RenderShadow(D3DXMATRIX& matViewProj, D3DXMATRIX& matWrap)
	{
		if (!_sceneNode || !_bVisible)
			return;

		BOOL ret = FALSE;
		CEngine* engine = GetEngine();
		CRenderDevice* rd = engine->GetRenderDevice();
		rd->BindRenderInput(_renderInputShadow);

		D3DXMATRIX mvp = matViewProj;
		_shader->SetFloatArray(SCN_MAT_WORLD_VIEW_PROJ, (float*)&mvp, 16);

		DWORD passNum;
		ret = _shader->TechniqueBegin(TECH_TERRAIN_SHADOW, passNum);
		MK_PROCESS_ERROR(ret);

		for (DWORD pass = 0; pass < passNum; ++pass)
		{
			if (_shader->PassBegin(pass))
			{
				ret = rd->DrawPrimitive(PT_TRIANGLELIST, 0, 0, TERRAIN_REGION_VERTEX_COUNT, 0, TERRAIN_REGION_FACE_COUNT);
				MK_PROCESS_ERROR(ret);

				_shader->PassEnd();
			}
		}

		_shader->TechniqueEnd();

Exit0:
		return;
	}

	void CEntityTerrainRegion::Render()
	{
		if (!_sceneNode || !_bVisible || !_pEntityTerrain)
			return;

		BOOL ret = FALSE;

		CEngine* engine = GetEngine();
		CCamera* camera = engine->GetCurrentCamera();
		CRenderDevice* rd = engine->GetRenderDevice();
		CMaterial* material = _pEntityTerrain->GetMaterial();

		rd->BindRenderInput(_renderInput);

		D3DXMATRIX mvp = engine->GetCurrentCamera()->GetViewProjMatrix();
		_shader->SetMatrix(SCN_MAT_WORLD_VIEW_PROJ, &mvp);
		_shader->SetMatrix(SCN_MAT_WORLD_VIEW, (D3DXMATRIX*)&camera->GetViewMatrix());
		_shader->SetFloatArray(SCN_EYS_POS, (float*)&camera->GetPosition(), 3);

		//if (engine->IsRenderShadowEnable())
		//{
		//    // Compute the texture matrix
		//    D3DXMATRIX matTexAdj(
		//        -0.5f,	0.0f,	0.0f,	0.0f,
		//        0.0f,   -0.5f,  0.0f,	0.0f,
		//        0.0f,	0.0f,	1.0f,	0.0f,
		//        -0.5f,  0.5f,   0.0f,   1.0f);

		//    D3DXMATRIX matTexture = engine->GetDirectionalLight()->GetViewProjMatrix() * matTexAdj;
		//    _shader->SetFloatArray( SCN_MAT_PROJ_TEXTURE, (float*)&matTexture, 16);

		//    _shader->SetTexture(SCN_SHADOW_MAP_TEXTURE, engine->GetShadowMap());
		//}

		for (DWORD i = 0; i < _mapping.GetMapCount(); ++i)
		{
			CTextureMapping::TextureData texData = NULL;
			ret = _mapping.GetMapTextureData(i, texData);
			MK_PROCESS_ERROR(ret);

			std::string texName;
			ret = _mapping.GetMapTextureName(i, texName);
			MK_PROCESS_ERROR(ret);

			CTexture2D* tex = (CTexture2D*)texData;
			ret = _shader->SetTexture(texName.c_str(), tex);
			MK_PROCESS_ERROR(ret);
		}

		for (DWORD i = 0; i < _layerMapping.GetMapCount(); ++i)
		{
			CTextureMapping::TextureData texData = NULL;
			ret = _layerMapping.GetMapTextureData(i, texData);
			MK_PROCESS_ERROR(ret);

			std::string texName;
			ret = _layerMapping.GetMapTextureName(i, texName);
			MK_PROCESS_ERROR(ret);

			CTexture2D* tex = (CTexture2D*)texData;
			ret = _shader->SetTexture(texName.c_str(), tex);
			MK_PROCESS_ERROR(ret);
		}

		material->SetMaterial(_scene, _shader, FALSE);

		const char* techniqueName = __GetTechniqueName();
		MK_PROCESS_ERROR(techniqueName);

		DWORD passNum = 0;
		ret = _shader->TechniqueBegin(techniqueName, passNum);
		MK_PROCESS_ERROR(ret);

		for (DWORD pass = 0; pass < passNum; ++pass)
		{
			if (_shader->PassBegin(pass))
			{
				ret = rd->DrawPrimitive(PT_TRIANGLELIST, 0, 0, TERRAIN_REGION_VERTEX_COUNT, 0, TERRAIN_REGION_FACE_COUNT);
				MK_PROCESS_ERROR(ret);

				_shader->PassEnd();
			}
		}

		_shader->TechniqueEnd();

		material->RestoreMaterial();

Exit0:
		return;
	}

	const char* CEntityTerrainRegion::__GetTechniqueName()
	{
		int layerCount = _layerMapping.GetMapCount();
		if (CMaterial::GetFogProperty()->IsNeedToSet())
		{
			switch (layerCount)
			{
			case 1:
				return TECH_TERRAIN_FOG_LAYER1;
			case 2:
				return TECH_TERRAIN_FOG_LAYER2;
			case 3:
				return TECH_TERRAIN_FOG_LAYER3;
			case 4:
				return TECH_TERRAIN_FOG_LAYER4;
			default:
				return NULL;
			}
		}
		else
		{
			switch (layerCount)
			{
			case 1:
				return TECH_TERRAIN_LAYER_1;
			case 2:
				return TECH_TERRAIN_LAYER_2;
			case 3:
				return TECH_TERRAIN_LAYER_3;
			case 4:
				return TECH_TERRAIN_LAYER_4;
			default:
				return NULL;
			}
		}
	}

	BOOL CEntityTerrainRegion::GetVertexPosition(D3DXVECTOR3& pos, DWORD x, DWORD y)
	{
		return _coreTerrainRegion->GetVertexPosition(pos, x, y);
	}

	BOOL CEntityTerrainRegion::GetGroundHeight(float& height, float x, float y, float offsetX, float offsetY)
	{
		if (x < 0 || y < 0)
			return FALSE;

		DWORD cellX = (DWORD)(offsetX / TERRAIN_CELL_WIDTH);
		DWORD cellY = (DWORD)(offsetY / TERRAIN_CELL_HEIGHT);
		if (cellX > TERRAIN_REGION_WIDTH - 1 || cellY > TERRAIN_REGION_HEIGHT - 1)
			return FALSE;

		float fx = (offsetX - cellX * TERRAIN_CELL_WIDTH) / TERRAIN_CELL_WIDTH;
		float fy = (offsetY - cellY * TERRAIN_CELL_HEIGHT) / TERRAIN_CELL_HEIGHT;

		D3DXVECTOR3 v1, v2, v3;
		if (fy < 1.0f - fx)
		{
			if (!GetVertexPosition(v1, cellX, cellY))
				return FALSE;
			if (!GetVertexPosition(v2, cellX + 1, cellY))
				return FALSE;
			if (!GetVertexPosition(v3, cellX, cellY + 1))
				return FALSE;
		}
		else
		{
			if (!GetVertexPosition(v1, cellX + 1, cellY + 1))
				return FALSE;
			if (!GetVertexPosition(v2, cellX + 1, cellY))
				return FALSE;
			if (!GetVertexPosition(v3, cellX, cellY + 1))
				return FALSE;
		}

		D3DXPLANE plane;
		D3DXPlaneFromPoints(&plane, &v1, &v2, &v3);
		height = (-plane.d - plane.a * x - plane.c * y) / plane.b;

		return TRUE;
	}

	BOOL CEntityTerrainRegion::PaintHeight(CTerrainHeightBrush* brush)
	{
		brush->Paint(_coreTerrainRegion->_terrainData);
		_coreTerrainRegion->RecalculateFaceNormal();

		BOOL ret = UpdatePositionVertexBuffer();
		MK_PROCESS_ERROR(ret);

		UpdateLocalAABB();
		return TRUE;
Exit0:
		return FALSE;
	}


	BOOL CEntityTerrainRegion::AddTextureMap(const char* name, CTexture2D* pTexture, BOOL isLayer)
	{
		CTextureMapping::Map map;

		map.fileName = pTexture->GetResourceName();
		map.textureName = name;
		map.textureData = (CTextureMapping::TextureData)pTexture;

		if (isLayer)
			_layerMapping.AddMap(map);
		else
			_mapping.AddMap(map);

		_vecTexture.push_back(pTexture);
		pTexture->AddRef();

		return TRUE;
	}

	BOOL CEntityTerrainRegion::AddTextureMapFromFile(const char* name, const char* fileName, BOOL isLayer)
	{
		CTextureMapping::Map map;

		map.fileName = fileName;
		map.textureName = name;

		CTexture2D* tex = NULL;
		CResourceManager* rm = GetEngine()->GetResourceManager();
		BOOL ret = rm->GetTexture2DFromFile(fileName, tex);
		MK_PROCESS_ERROR(ret);

		map.textureData = (CTextureMapping::TextureData)tex;
		_vecTexture.push_back(tex);

		if (isLayer)
			_layerMapping.AddMap(map);
		else
			_mapping.AddMap(map);

		return TRUE;
Exit0:
		return FALSE;
	}



	BOOL CEntityTerrainRegion::SaveTextureMap(const char* name, const char* fileName)
	{
		CTexture2D* tex = NULL;
		BOOL ret = _mapping.GetMapTextureDataByName(name, (CTextureMapping::TextureData&)tex);
		MK_PROCESS_ERROR(ret);

		ret = tex->SaveToFile(fileName);
		MK_PROCESS_ERROR(ret);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::FillTextureMapA8R8G8B8(const char* name, DWORD color)
	{
		BYTE* pData = NULL;
		DWORD pitch = 0;

		CTexture2D* tex = NULL;
		BOOL ret = _mapping.GetMapTextureDataByName(name, (CTextureMapping::TextureData&)tex);
		MK_PROCESS_ERROR(ret);

		MK_PROCESS_ERROR(tex->GetFormat() == PF_A8R8G8B8);

		ret = tex->LockRect((void**)&pData, &pitch, NULL);
		MK_PROCESS_ERROR(ret);

		for (DWORD y = 0; y < tex->GetWidth(); ++y)
			for (DWORD x = 0; x < tex->GetHeight(); ++x)
				*(DWORD*)(pData + pitch * y + x * sizeof(DWORD)) = color;

		ret = tex->Unlock();
		MK_PROCESS_ERROR(ret);

		return TRUE;

Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::FillTextureMapR32F(const char* name, float color)
	{
		BYTE* pData = NULL;
		DWORD pitch = 0;

		CTexture2D* tex = NULL;
		BOOL ret = _mapping.GetMapTextureDataByName(name, (CTextureMapping::TextureData&)tex);
		MK_PROCESS_ERROR(ret);

		MK_PROCESS_ERROR(tex->GetFormat() == PF_R32F);

		ret = tex->LockRect((void**)&pData, &pitch, NULL);
		MK_PROCESS_ERROR(ret);

		for (DWORD y = 0; y < tex->GetWidth(); ++y)
			for (DWORD x = 0; x < tex->GetHeight(); ++x)
				*(float*)(pData + pitch * y + x * sizeof(float)) = color;

		ret = tex->Unlock();
		MK_PROCESS_ERROR(ret);

		return TRUE;

Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::FillTextureMapL8(const char* name, BYTE color)
	{
		BYTE* pData = NULL;
		DWORD pitch = 0;

		CTexture2D* tex = NULL;
		BOOL ret = _mapping.GetMapTextureDataByName(name, (CTextureMapping::TextureData&)tex);
		MK_PROCESS_ERROR(ret);

		MK_PROCESS_ERROR(tex->GetFormat() == PF_L8);

		ret = tex->LockRect((void**)&pData, &pitch, NULL);
		MK_PROCESS_ERROR(ret);

		for (DWORD y = 0; y < tex->GetWidth(); ++y)
			for (DWORD x = 0; x < tex->GetHeight(); ++x)
				*(pData + pitch * y + x) = color;

		ret = tex->Unlock();
		MK_PROCESS_ERROR(ret);

		return TRUE;

Exit0:
		return FALSE;
	}

	CTexture2D* CEntityTerrainRegion::GetTextureMap(const char* name, BOOL isLayer)
	{
		if (isLayer)
		{
			CTextureMapping::TextureData tex = NULL;
			BOOL ret = _layerMapping.GetMapTextureDataByName(name, tex);
			MK_PROCESS_ERROR(ret);

			return (CTexture2D*)tex;
		}
		else
		{
			CTextureMapping::TextureData tex = NULL;
			BOOL ret = _mapping.GetMapTextureDataByName(name, tex);
			MK_PROCESS_ERROR(ret);

			return (CTexture2D*)tex;
		}

Exit0:
		return NULL;
	}

	DWORD CEntityTerrainRegion::GetLayerCount()
	{
		return _layerMapping.GetMapCount();
	}

	BOOL CEntityTerrainRegion::GetLayerMappingInfo(DWORD idx, /* out */ std::string& texName, /* out */ std::string& texFileName)
	{
		if (!_layerMapping.GetMapTextureName(idx, texName))
			return FALSE;

		if (!_layerMapping.GetMapTextureFile(idx, texFileName))
			return FALSE;

		return TRUE;
	}

	BOOL CEntityTerrainRegion::GetMappingInfoByName(const char* name, /* out */ std::string& texFileName)
	{
		return _mapping.GetMapTextureFileByName(name, texFileName);
	}

	BOOL CEntityTerrainRegion::ClearLayer(DWORD layer)
	{
		CTexture2D* tex = NULL;
		int ret = _mapping.GetMapTextureDataByName(__LAYER_BLEND_TEX_NAME, (CTextureMapping::TextureData&)tex);
		MK_PROCESS_ERROR(ret);

		MK_PROCESS_ERROR(tex->GetFormat() == PF_A8R8G8B8);

		BYTE* pData = NULL;
		DWORD pitch;
		ret = tex->LockRect((void**)&pData, &pitch, NULL);
		MK_PROCESS_ERROR(ret);

		DWORD width = tex->GetWidth();
		DWORD height = tex->GetHeight();

		for (DWORD ty = 0; ty < height; ++ty)
		{
			for (DWORD tx = 0; tx < width; ++tx)
			{
				DWORD* color = (DWORD*)(pData + ty * pitch + tx * 4);
				if (layer == 0)
					*color = (*color) & 0xff00ffff;
				else if (layer == 1)
					*color = (*color) & 0xffff00ff;
				else if (layer == 2)
					*color = (*color) & 0xffffff00;
				else if (layer == 3)
					*color = (*color) & 0x00ffffff;
			}
		}

		tex->Unlock();

		return ret;

Exit0:
		return FALSE;
	}

	BOOL CEntityTerrainRegion::__CheckLayerEmpty(int layer)
	{
		CTexture2D* tex = NULL;
		int ret = _mapping.GetMapTextureDataByName(__LAYER_BLEND_TEX_NAME, (CTextureMapping::TextureData&)tex);
		MK_PROCESS_ERROR(ret);

		MK_PROCESS_ERROR(tex->GetFormat() == PF_A8R8G8B8);

		BYTE* pData = NULL;
		DWORD pitch;
		ret = tex->LockRect((void**)&pData, &pitch, NULL);
		MK_PROCESS_ERROR(ret);

		DWORD width = tex->GetWidth();
		DWORD height = tex->GetHeight();

		ret = TRUE;
		for (DWORD ty = 0; ty < height; ++ty)
		{
			for (DWORD tx = 0; tx < width; ++tx)
			{
				DWORD color = *(DWORD*)(pData + ty * pitch + tx * 4);
				DWORD r = ((color & 0x00ff0000) >> 16);
				DWORD g = ((color & 0x0000ff00) >> 8);
				DWORD b = (color & 0x000000ff);
				DWORD a = color >> 24;

				if (layer == 0)
				{
					if (r)
					{
						ret = FALSE;
						break;
					}
				}
				else if (layer == 1)
				{
					if (g)
					{
						ret = FALSE;
						break;
					}
				}
				else if (layer == 2)
				{
					if (b)
					{
						ret = FALSE;
						break;
					}
				}
				else if (layer == 3)
				{
					if (a)
					{
						ret = FALSE;
						break;
					}
				}
			}

			if (!ret)
				break;
		}

		tex->Unlock();

		return ret;

Exit0:
		return FALSE;
	}

	int CEntityTerrainRegion::__DecideLayer(const char* fileName)
	{
		char buf[64];
		int mapCount = (int)_layerMapping.GetMapCount();
		for (int i = 0; i < mapCount; ++i)
		{
			std::string texFileName;
			sprintf(buf, "%s%d", __LAYER_TEX_NAME_PREFIX, i + 1);
			int ret = _layerMapping.GetMapTextureFileByName(buf, texFileName);
			MK_PROCESS_ERROR(ret);

			if (_stricmp(texFileName.c_str(), fileName) == 0)
				return i;
		}

		if (_layerMapping.GetMapCount() == 4)
		{
			for (int i = 0; i < 4; ++i)
			{
				if (__CheckLayerEmpty(i))
				{
					CTexture2D* tex = NULL;
					sprintf(buf, "%s%d", __LAYER_TEX_NAME_PREFIX, i + 1);
					int ret = _layerMapping.GetMapTextureDataByName(buf, (CTextureMapping::TextureData&)tex);
					MK_PROCESS_ERROR(ret);

					__RemoveTexture(tex);

					ret = _layerMapping.RemoveMapByName(buf);
					MK_PROCESS_ERROR(ret);

					ret = AddTextureMapFromFile(buf, fileName, TRUE);
					MK_PROCESS_ERROR(ret);

					return i;
				}
			}

			return -1;
		}

		sprintf(buf, "%s%d", __LAYER_TEX_NAME_PREFIX, _layerMapping.GetMapCount() + 1);
		int ret = AddTextureMapFromFile(buf, fileName, TRUE);
		MK_PROCESS_ERROR(ret);

		return (int)_layerMapping.GetMapCount() - 1;

Exit0:
		return -1;
	}

	void CEntityTerrainRegion::__RemoveTexture(CTexture2D* tex)
	{
		std::vector<CTexture2D*>::iterator i = _vecTexture.begin();
		while (i != _vecTexture.end())
		{
			if (tex == (*i))
			{
				SAFE_RELEASE(tex);
				_vecTexture.erase(i);
				return;
			}

			++i;
		}
	}

	BOOL CEntityTerrainRegion::PaintLayer(CTerrainLayerBrush* brush, const char* fileName)
	{
		float x, y;
		brush->GetPosition(x, y);

		int layer = __DecideLayer(fileName);
		if (layer < 0)
			return FALSE;

		brush->Paint(_vecTexture[0], layer, 
			x - _regionX * TERRAIN_REGION_WIDTH_SIZE, 
			y - _regionY * TERRAIN_REGION_HEIGHT_SIZE);

		return TRUE;
	}

	BOOL CEntityTerrainRegion::CollideWithRay(D3DXVECTOR3& pos, float x, float y, float z, D3DXVECTOR3& dir)
	{
		if (!AABBCollisionWithRay(pos, _worldAABB, x, y, z, dir))
			return FALSE;

		return _coreTerrainRegion->CollideWithRay(pos, x, y, z, dir);
	}
}